﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Collections;
using System.Windows.Data;
using System.Collections.Specialized;

namespace Gmantis.Controls
{
    [StyleTypedProperty(Property = "ItemContainerStyle", StyleTargetType = typeof(ListBoxItem))]
    public class ListBox : ListViewer
    {
        // Fields
        private DragDirection _dragDirection;
        private ListBoxItem _draggedItem;
        private int _draggedItemIndex;
        private bool _draggedItemIsSelected;
        private DragHelper _dragHelper;
        //internal new bool _isLoaded;
        private List<object> _selectedItems;
        private RangeSelectionManager _selectionManager;
        private bool _updatingSelectionDP;
        private bool _updatingSelectionManager;
        internal const double GESTURE_THRESHOLD = 10.0;
        internal const double HINTING_THRESHOLD = 20.0;
        public new static readonly DependencyProperty MouseOverBrushProperty = DependencyProperty.Register("MouseOverBrush", typeof(Brush), typeof(ListBox), null);
        public static readonly DependencyProperty SelectedBackgroundProperty = DependencyProperty.Register("SelectedBackground", typeof(Brush), typeof(ListBox), null);
        public static readonly DependencyProperty SelectedItemProperty = DependencyProperty.Register("SelectedItem", typeof(object), typeof(ListBox), new PropertyMetadata(new PropertyChangedCallback(ListBox.OnSelectedItemPropertyChanged)));
        public static readonly DependencyProperty SelectedItemsProperty = DependencyProperty.Register("SelectedItems", typeof(IEnumerable), typeof(ListBox), new PropertyMetadata(new PropertyChangedCallback(ListBox.OnSelectedItemsPropertyChanged)));
        public static readonly DependencyProperty SelectionModeProperty = DependencyProperty.Register("SelectionMode", typeof(SelectionMode), typeof(ListBox), new PropertyMetadata(SelectionMode.Multiple, new PropertyChangedCallback(ListBox.OnSelectionModePropertyChanged)));

        // Events
        public event EventHandler SelectionChanged;

        // Methods
        public ListBox()
        {
            base.DefaultStyleKey = typeof(ListBox);
            this.SetCustomDefaultValues();
        }

        private void BringToFront(ListBoxItem listBoxItem)
        {
            foreach (UIElement element in base.Panel.Children)
            {
                Canvas.SetZIndex(element, (element == listBoxItem) ? 1 : 0);
            }
        }

        public void ClearSelection()
        {
            this._updatingSelectionManager = true;
            this._selectionManager.ClearSelection();
            this._updatingSelectionManager = false;
            this.SyncSelectedItems();
        }

        protected override DependencyObject GetContainerForItemOverride()
        {
            ListBoxItem item = new ListBoxItem();
            item.SetBinding(FrameworkElement.StyleProperty, new Binding().From<ListBox>(this, x => x.ItemStyle));
            return item;
        }

        protected override void InitializePanel()
        {
            base.InitializePanel();
            if (this._dragHelper != null)
            {
                this._dragHelper.FinalizeDrag();
            }
            this._dragHelper = new DragHelper(base.Panel, DragHelperMode.Inertia | DragHelperMode.TranslateRailX | DragHelperMode.TranslateRailY | DragHelperMode.TranslateX | DragHelperMode.TranslateY, 10.0, true, true, true, false);
            this._dragHelper.DragStarted += new EventHandler<DragStartedEventArgs>(this.OnDragStarted);
            this._dragHelper.DragDelta += new EventHandler<DragDeltaEventArgs>(this.OnDragDelta);
            this._dragHelper.DragCompleted += new EventHandler<DragCompletedEventArgs>(this.OnDragCompleted);
            MouseHelper helper = new MouseHelper(base.Panel);
            helper.ContextMenuInvoked += (s, e) => this.OnRightClick(s, e as MouseButtonEventArgs);
            this.UpdatePanelInteraction();
        }

        protected override bool IsItemItsOwnContainerOverride(object item)
        {
            return (item is ListBoxItem);
        }

        private void OnDragCompleted(object sender, DragCompletedEventArgs e)
        {
            if (((e.PointerDeviceType == PointerDeviceType.Touch) && this.CrossSlideEnabled) && (this._draggedItem != null))
            {
                this._draggedItem.StopHinting();
            }
        }

        private void OnDragDelta(object sender, DragDeltaEventArgs e)
        {
            if (((e.PointerDeviceType == PointerDeviceType.Touch) && this.CrossSlideEnabled) && (this.IsCrossSlide && (this._draggedItem != null)))
            {
                bool isSelected = this._draggedItemIsSelected;
                if (e.IsInertial)
                {
                    e.Complete();
                    isSelected = !this._draggedItemIsSelected;
                    if (this._draggedItem.IsSelected != isSelected)
                    {
                        this._selectionManager.HandleSelectionChange(this._draggedItemIndex, isSelected);
                    }
                }
                else
                {
                    this.BringToFront(this._draggedItem);
                    double num = (base.Orientation == Orientation.Vertical) ? e.CumulativeTranslation.X : e.CumulativeTranslation.Y;
                    if (Math.Abs(num) >= 20.0)
                    {
                        isSelected = !this._draggedItemIsSelected;
                    }
                    this._draggedItem.Hinting((base.Orientation == Orientation.Horizontal) ? Orientation.Vertical : Orientation.Horizontal, num);
                    if (this._draggedItem.IsSelected != isSelected)
                    {
                        this._selectionManager.HandleSelectionChange(this._draggedItemIndex, isSelected);
                    }
                }
                e.Handled = true;
            }
        }

        private void OnDragStarted(object sender, DragStartedEventArgs e)
        {
            if ((e.PointerDeviceType == PointerDeviceType.Touch) && this.CrossSlideEnabled)
            {
                this._dragDirection = e.Direction;
                if (this.IsCrossSlide)
                {
                    this._draggedItem = base.Panel.HitTest(e.Origin).FirstOrDefault<UIElement>(elem => (elem is ListBoxItem)) as ListBoxItem;
                    if (this._draggedItem != null)
                    {
                        this._draggedItemIndex = base.ItemContainerGenerator.IndexFromContainer(this._draggedItem);
                        this._draggedItemIsSelected = this._selectionManager.Contains(this._draggedItemIndex);
                    }
                    e.Handled = true;
                }
            }
        }

        protected override void OnItemsChanged(System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
        {
            base.OnItemsChanged(e);
            switch (e.Action)
            {
                case NotifyCollectionChangedAction.Add:
                    this._selectionManager.Shift(e.NewStartingIndex);
                    break;

                case NotifyCollectionChangedAction.Remove:
                    this._selectionManager.Shrink(e.OldStartingIndex);
                    break;

                case NotifyCollectionChangedAction.Reset:
                    this._selectionManager.ClearSelection();
                    break;
            }
            this.SyncSelectedItems();
        }

        private void OnRightClick(object sender, MouseButtonEventArgs e)
        {
            if (!e.Handled && (base.Panel != null))
            {
                ListBoxItem container = base.Panel.HitTest(e.GetPosition(null)).FirstOrDefault<UIElement>(elem => (elem is ListBoxItem)) as ListBoxItem;
                if (container != null)
                {
                    int index = base.ItemContainerGenerator.IndexFromContainer(container);
                    this._selectionManager.HandleSelectionChange(index, !container.IsSelected);
                    e.Handled = true;
                }
            }
        }

        private void OnSelectedItemChanged(object oldValue)
        {
            if (!this._updatingSelectionDP)
            {
                this._selectedItems = new List<object> { this.SelectedItem };
                new List<int>();
                this._updatingSelectionManager = true;
                this._selectionManager.ClearSelection();
                this._selectionManager.HandleSelectionChange(base.Items.IndexOf(this.SelectedItem), true);
                this._updatingSelectionManager = false;
                this.SyncSelectedItems();
            }
        }

        private static void OnSelectedItemPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            ListBox box = d as ListBox;
            object oldValue = e.OldValue;
            box.OnSelectedItemChanged(oldValue);
        }

        private void OnSelectedItemsChanged(IEnumerable oldValue)
        {
            if (!this._updatingSelectionDP)
            {
                this._selectedItems = new List<object>(this.SelectedItems.Cast<object>());
                new List<int>();
                this._updatingSelectionManager = true;
                this._selectionManager.ClearSelection();
                foreach (object obj2 in this._selectedItems)
                {
                    int index = base.Items.IndexOf(obj2);
                    if (index != -1)
                    {
                        this._selectionManager.HandleSelectionChange(index, true);
                    }
                }
                this._updatingSelectionManager = false;
                this.SyncSelectedItems();
            }
        }

        private static void OnSelectedItemsPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            ListBox box = d as ListBox;
            IEnumerable oldValue = (IEnumerable)e.OldValue;
            box.OnSelectedItemsChanged(oldValue);
        }

        protected virtual void OnSelectionChanged(object sender, EventArgs e)
        {
            if (this.SelectionChanged != null)
            {
                this.SelectionChanged(this, e);
            }
        }

        private void OnSelectionChanged(object sender, SelectionChangedEventArgs<int> e)
        {
            foreach (int num in e.RemovedItems)
            {
                ListBoxItem item = base.ItemContainerGenerator.ContainerFromIndex(num) as ListBoxItem;
                if (item != null)
                {
                    item.IsSelected = false;
                }
            }
            foreach (int num2 in e.AddedItems)
            {
                ListBoxItem item2 = base.ItemContainerGenerator.ContainerFromIndex(num2) as ListBoxItem;
                if (item2 != null)
                {
                    item2.IsSelected = true;
                }
            }
            if (!this._updatingSelectionManager)
            {
                this.SyncSelectedItems();
            }
        }

        private void OnSelectionModeChanged(SelectionMode oldValue)
        {
            this.UpdatePanelInteraction();
            this._selectionManager.SelectionMode = this.SelectionMode;
        }

        private static void OnSelectionModePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            ListBox box = d as ListBox;
            SelectionMode oldValue = (SelectionMode)e.OldValue;
            box.OnSelectionModeChanged(oldValue);
        }

        protected override void PrepareContainerForItemOverride(DependencyObject element, object item)
        {
            base.PrepareContainerForItemOverride(element, item);
            ListBoxItem target = element as ListBoxItem;
            int index = base.ItemContainerGenerator.IndexFromContainer(element);
            target.IsSelected = this._selectionManager.Contains(index);
            target.SetBinding<ListBox>(ListBoxItem.MouseOverBrushProperty, this, x => x.MouseOverBrush);
            target.SetBinding<ListBox>(ListBoxItem.SelectedBackgroundProperty, this, x => x.SelectedBackground);
        }

        private void SetCustomDefaultValues()
        {
            this._selectedItems = new List<object>();
            this._selectionManager = new RangeSelectionManager();
            this._selectionManager.SelectionMode = SelectionMode.Multiple;
            this._selectionManager.SelectionChanged += new EventHandler<SelectionChangedEventArgs<int>>(this.OnSelectionChanged);
        }

        private void SyncSelectedItems()
        {
            object[] second = this._selectedItems.ToArray();
            this._selectedItems.Clear();
            foreach (int num in this._selectionManager.SelectedItems)
            {
                this._selectedItems.Add(base.Items[num]);
            }
            if ((this.SelectedItems == null) || !this._selectedItems.SequenceEqual<object>(second))
            {
                this._updatingSelectionDP = true;
                this.SelectedItem = this._selectedItems.FirstOrDefault<object>();
                this.SelectedItems = this._selectedItems.ToArray();
                this.OnSelectionChanged(this, new EventArgs());
                this._updatingSelectionDP = false;
            }
        }

        private void UpdatePanelInteraction()
        {

        }

        // Properties
        private bool CrossSlideEnabled
        {
            get { return base.IsSwipeEnabled && (this.SelectionMode != Controls.SelectionMode.None); }
        }

        private bool IsCrossSlide
        {
            get
            {
                return (((base.Orientation == System.Windows.Controls.Orientation.Vertical) && (this._dragDirection == DragDirection.Horizontal)) ||
                    ((base.Orientation == System.Windows.Controls.Orientation.Horizontal) && (this._dragDirection == DragDirection.Vertical)));
            }
        }

        public new Brush MouseOverBrush
        {
            get { return (Brush)GetValue(MouseOverBrushProperty); }
            set { SetValue(MouseOverBrushProperty, value); }
        }

        public Brush SelectedBackground
        {
            get { return (Brush)GetValue(SelectedBackgroundProperty); }
            set { SetValue(SelectedBackgroundProperty, value); }
        }

        public object SelectedItem
        {
            get { return GetValue(SelectedItemProperty); }
            set { SetValue(SelectedItemProperty, value); }
        }

        public IEnumerable SelectedItems
        {
            get { return (IEnumerable)GetValue(SelectedItemsProperty); }
            set { SetValue(SelectedItemsProperty, value); }
        }

        public SelectionMode SelectionMode
        {
            get { return (SelectionMode)GetValue(SelectionModeProperty); }
            set { SetValue(SelectionModeProperty, value); }
        }
    }
}
